package Applet;

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import java.rmi.RemoteException;
import java.rmi.registry.*;
import javax.swing.Timer;
import Middleware.*;

class Position {
	private int x;
	private int y;
	
	public Position(int x, int y) {
		this.x=x;
		this.y=y;
	}
	
	public int getX() {
		return x;
	}

	public int getY() {
		return y;
	}
}

class CardsFactory {
	private Map<String,Image> cards;
	private static CardsFactory instance = new CardsFactory();
	
	public void addCardImage(String name, Image img) {
		cards.put(name, img);
	}
	
	public static CardsFactory getInstance() {
		return instance;
	}
	
	public Image getCardImage(String name) {
		if(cards.containsKey(name))
			return (Image)cards.get(name);
		return (Image)cards.get("BACK");
	}
	
	private CardsFactory() {
		cards = new HashMap<String,Image>();
	}
}

class Player {
	private Position pos;
	private IPlayer player;
	
	public Player(int x, int y) {
		this.pos = new Position(x,y);
	}
	
	public void setPlayer(IPlayer player) {
		this.player = player;
	}
	
	public void paint(Graphics g, ImageObserver observer) {
		int x = pos.getX();
		int y = pos.getY();
		if(player != null) {
			String name = "";
			if(player.isMyTurn()) {
				name = "--> " + player.getUsername() + " (" + points() + ")";
			} else {
				name = player.getUsername() + " (" + points() + ")";
			}
			if(player.isWinner()) {
				name = "WINNER " + name;
			}
			g.drawString(name, x, y-5);
			if(player.getCards() != null) {
				for(ICard card : player.getCards()) {
					g.drawImage(CardsFactory.getInstance().getCardImage(card.getName()), x, y, observer);
					y += 25;
				}
			}
		}
	}
	
	public void hide_cards() {
		if(player.showCards()) {
			return;
		}
		ICard unknown = new Card('U','U');
		for(int i=1; i<player.getCards().size(); i++) {
			player.getCards().set(i, unknown);
		}
	}
	
	double getCredit() {
		if(player == null) {
			return 0;
		}
		return player.getCredit();
	}
	
	private int points()
	{
		if(this.player == null) {
			return 0;
		}
		ArrayList<ICard> cards = player.getCards();
		int p = 0;
		for(ICard c : cards)
		{
			switch(c.getValue())
			{
			case '2': p+=2; break;
			case '3': p+=3; break;
			case '4': p+=4; break;
			case '5': p+=5; break;
			case '6': p+=6; break;
			case '7': p+=7; break;
			case '8': p+=8; break;
			case '9': p+=9; break;
			case 'T': p+=10; break;
			case 'J': p+=10; break;
			case 'Q': p+=10; break;
			case 'K': p+=10; break;
			case 'A': p+=11; break;
			}
		}
		if(p==22 && cards.size() == 2) {
			p = 21;
		}
		if(p>21) {
			for(ICard c : cards) {
				if(c.getValue()=='A') {
					p-=10;
					if(p<=21) {
						break;
					}
				}
			}
		}
		return p;
	}
}

public class applet extends Applet {
	private static final long serialVersionUID = -3298908131957438256L;
	private Player[] players;
	private IServer server;
	private IPlayer player;
	private IRoom[] rooms;
	private IRoom room;
	private Registry registry;
	
	private String username, password;
	private int current_timestamp;
	private Date last_time_checked;
	
	private Color backgroundColor;
	
	// LOGIN ELEMENTS
	private TextField t_username, t_password;
	private Label l_username, l_password;
	private Button b_login;
	
	// VIEW ROOMS ELEMENTS
	private TextArea l_rooms;
	private TextField t_room;
	private Button b_new_room;
	private Button b_choose_room;
	
	// IN GAME ELEMENTS
	private Button b_hit_me;
	private Button b_stand;
	private Button b_double;
	private Button b_leave_room;
	private TextField t_bet_sum;
	private Button b_bet;
	
	// APPLET STATE
	private static int SERVER_NOT_CONNECTED = 1;
	private static int FIRST_LOGIN = 2;
	private static int FAILED_LOGIN = 3;
	private static int VIEW_ROOMS = 4;
	private static int IN_GAME = 5;
	private int state;
	
	private void init_cards() {
		char []cards_value = {'2', '3', '4', '5', '6', '7', '8', '9', 't', 'j', 'q', 'k', 'a'};
		char []cards_color = {'c', 'd', 'h', 's'};
		String path, name;
		Image img;
		for(int i=0; i<cards_value.length; i++) {
			for(int j=0; j<cards_color.length; j++) {
				path = "cards/" + cards_value[i] + cards_color[j] + ".gif";
				name = "" + Character.toUpperCase(cards_value[i]) + Character.toUpperCase(cards_color[j]);
				img = getImage(getDocumentBase(), path);
				CardsFactory.getInstance().addCardImage(name, img);
			}
		}
		img = getImage(getDocumentBase(), "cards/back.jpg");
		CardsFactory.getInstance().addCardImage("BACK", img);
	}
	
	private void init_all() {
		// LOGIN ELEMENTS
		l_username = new Label("Username:");
		l_username.setBackground(backgroundColor);
		l_username.setVisible(false);
		add(l_username);
		
		t_username = new TextField(15);
		t_username.setVisible(false);
		add(t_username);
		
		l_password = new Label("Password:");
		l_password.setBackground(backgroundColor);
		l_password.setVisible(false);
		add(l_password);
		
		t_password = new TextField(15);
		t_password.setEchoChar('*');
		t_password.setVisible(false);
		add(t_password);
		
		b_login = new Button("Log in");
		b_login.setVisible(false);
		add(b_login);
		
		// IN GAME ELEMENTS
		b_hit_me = new Button("Hit Me");
		b_hit_me.setVisible(false);
		add(b_hit_me);
		
		b_stand = new Button("Stand");
		b_stand.setVisible(false);
		add(b_stand);

		b_double = new Button("Double");
		b_double.setVisible(false);
		add(b_double);
		
		b_leave_room = new Button("Leave Room");
		b_leave_room.setVisible(false);
		add(b_leave_room); 
		
		t_bet_sum = new TextField(5);
		t_bet_sum.setVisible(false);
		add(t_bet_sum); 
		
		b_bet = new Button("Bet");
		b_bet.setVisible(false);
		add(b_bet); 
		
		// VIEW ROOMS ELEMENTS
		b_new_room = new Button("Create new room");
		b_new_room.setVisible(false);
		add(b_new_room);
		
		l_rooms = new TextArea("Rooms text here------------------------------\n"+
				"-----------------------------------------------------\n"+
				"-----------------------------------------------------\n"+
				"-----------------------------------------------------\n"+
				"-----------------------------------------------------\n"+
				"-----------------------------------------------------\n"+
				"-----------------------------------------------------\n"+
				"-----------------------------------------------------\n"+
				"-----------------------------------------------------\n"+
				"-----------------------------------------------------\n"+
				"-----------------------------------------------------\n"+
				"-----------------------------------------------------\n"+
				"-----------------------------------------------------\n");
		l_rooms.setEnabled(false);
		l_rooms.setVisible(false);
		add(l_rooms);
		
		t_room = new TextField(20);
		t_room.setVisible(false);
		add(t_room);
		
		b_choose_room = new Button("Enter room");
		b_choose_room.setVisible(false);
		add(b_choose_room);
	}
	
	private void deinit_all() {
		deinit_login();
		deinit_game();
		deinit_rooms();
	}
	
	private void init_login() {
		l_username.setVisible(true);
		t_username.setVisible(true);
		l_password.setVisible(true);
		t_password.setVisible(true);
		b_login.setVisible(true);
		validate();
	}
	
	private void deinit_login() {
		l_username.setVisible(false);
		t_username.setVisible(false);
		l_password.setVisible(false);
		t_password.setVisible(false);
		b_login.setVisible(false);
		validate();
	}
	
	private void init_game() {
		b_hit_me.setVisible(true);
        b_stand.setVisible(true);
        b_double.setVisible(true);
        b_leave_room.setVisible(true);
        t_bet_sum.setVisible(true);
        b_bet.setVisible(true);
        validate();
	}
	
	private void deinit_game() {
		b_hit_me.setVisible(false);
        b_stand.setVisible(false);
        b_double.setVisible(false);
        b_leave_room.setVisible(false);
        t_bet_sum.setVisible(false);
        b_bet.setVisible(false);
        validate();
	}
	
	private void init_rooms() {
		b_new_room.setVisible(true);
		l_rooms.setVisible(true);
		t_room.setVisible(true);
		b_choose_room.setVisible(true);
		
		if(rooms != null && rooms.length != 0) {
			String text = "You have the following rooms (please enter the code): \n";
			for(IRoom room : rooms) {
				try {
					text += "[" + room.getRMIName() + "] - " + room.getDetails();
					t_room.setText(room.getRMIName());
				} catch(Exception e) {
					System.out.println("Exception: " + e);
				}
			}
			l_rooms.setText(text);
		} else {
			l_rooms.setText("Currently there are no rooms. Create a new room!");
			t_room.setVisible(false);
			b_choose_room.setVisible(false);
		}
		validate();
	}
	
	private void deinit_rooms() {
		b_new_room.setVisible(false);
		l_rooms.setVisible(false);
		t_room.setVisible(false);
		b_choose_room.setVisible(false);
		validate();
	}
	
	private void init_players() {
		players = new Player[6];
		//players[0] = new Player(100, 0);
		//players[1] = new Player(200, 0);
		//players[2] = new Player(300, 0);
		//players[3] = new Player(400, 150);
		//players[4] = new Player(300, 300);
		//players[5] = new Player(200, 300);
		//players[6] = new Player(100, 300);
		//players[7] = new Player(0, 150);
		
		players[0] = new Player(200, 50);
		players[1] = new Player(400, 150);
		players[2] = new Player(300, 300);
		players[3] = new Player(200, 300);
		players[4] = new Player(100, 300);
		players[5] = new Player(0, 150);
		
			/*
		for(Player p : players) {
			p.addCard(new Card('2', 'C'));
			p.addCard(new Card('3', 'D'));
			p.addCard(new Card('4', 'H'));
			p.addCard(new Card('u', 'S'));
		}*/
		
	}
	
	public void init() {
		player = null;
		backgroundColor = new Color(0, 255, 0);
		state = SERVER_NOT_CONNECTED;
		
		last_time_checked = new Date();
		current_timestamp = 0;
		
		init_cards();
		init_players();
		init_all();
		setBackground( backgroundColor );
		try {
			registry = LocateRegistry.getRegistry("localhost", 1099); // 1099 is default
			server = (IServer) registry.lookup("blackjack");
			state = FIRST_LOGIN;
			init_login();
		} catch(Exception e) {
			System.out.println("Connection to server failed " + e);
		}
    }
	
	public void start() {
		int delay = 200; //milliseconds
		ActionListener taskPerformer = new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				update();
			}
		};
		new Timer(delay, taskPerformer).start();
	}
	
    public void paint(Graphics g) {
    	if(state == SERVER_NOT_CONNECTED) {
    		g.drawString("Sorry, can't connect to the server, try again later!", 50, 50);
    		return;
    	}
    	if(state == IN_GAME) {
	    	for(Player p : players) {
	    		p.paint(g, this);
			}
	    	String v = "Credit: " + players[1].getCredit();
	    	g.drawString(v, 400, 50);
    	}
    	if(state == FAILED_LOGIN) {
    		g.drawString("Sorry, username or password missmatch!", 100, 100);
    	}
    }
    
    private IPlayer requestLogin(String username, String password) {
    	try {
			// Request a login
			return server.requestLogin(username, password);
		} catch(Exception e) {
			server = null;
			state = SERVER_NOT_CONNECTED;
			System.out.println("requestLogin() failed " + e);
		}
		return null;
    }
    
    private IRoom[] getRooms() {
    	try {
    		// Request the rooms
    		return server.getRooms();
    	} catch(Exception e) {
    		server = null;
			state = SERVER_NOT_CONNECTED;
			System.out.println("getRooms() failed " + e);
    	}
    	return null;
    }
    
    private IRoom createRoom() {
    	try {
    		// Request create a new room
    		IRoom temp = server.createRoom(player, 5);
    		return (IRoom) registry.lookup(temp.getRMIName());
    	} catch(Exception e) {
    		server = null;
    		state = SERVER_NOT_CONNECTED;
    		System.out.println("createRoom() failed " + e);
    	}
    	return null;
    }
    
    private IRoom getRoom(String RMIName) {
    	try {
    		// Request create a new room
    		return (IRoom) registry.lookup(RMIName);
    	} catch(Exception e) {
    		server = null;
    		state = SERVER_NOT_CONNECTED;
    		System.out.println("getRoom() failed " + e);
    	}
    	return null;
    }
    
    private boolean enterRoom(IRoom room) {
    	try {
    		// Request the rooms
    		return room.enter(username, password);
    	} catch(Exception e) {
    		server = null;
			state = SERVER_NOT_CONNECTED;
			System.out.println("enterRoom() failed " + e);
    	}
    	return false;
    }
    
    private void state_changed() {
    	deinit_all();
    	if(state == FIRST_LOGIN || state == FAILED_LOGIN) {
    		init_login();
    	}
    	if(state == VIEW_ROOMS) {
    		init_rooms();
    	}
    	if(state == IN_GAME) {
    		init_game();
    	}
    }
    
    private int getTimestamp() {
    	try {
    		return room.getTimestamp();
    	} catch(Exception e) {
    		server = null;
			state = SERVER_NOT_CONNECTED;
			System.out.println("getTimestamp() failed " + e);
    	}
    	return 0;
    }
    
    private ArrayList<IPlayer> getPlayers() {
    	try {
    		return room.getPlayers(username, password);
    	} catch(Exception e) {
    		server = null;
			state = SERVER_NOT_CONNECTED;
			System.out.println("getPlayers() failed " + e);
    	}
    	return null;
    }
    
    public void update() {
    	if(state == IN_GAME) {
	    	Date now = new Date();
	    	// update every second or so
	    	if(now.getTime() - last_time_checked.getTime() >= 1000) {
	    		last_time_checked = now;
	    		// update only if the remote object was modified
	    		int timestamp = getTimestamp();
	    		if(timestamp > current_timestamp) {
	    			current_timestamp = timestamp;
	    			ArrayList<IPlayer> players = getPlayers();
	    			if(players != null) {
		    			for(int i=0; i<players.size(); i++) {
		    				System.out.println(" - " + players.get(i).getUsername());
		    				this.players[i].setPlayer(players.get(i));
		    				if(i!=1) {
		    					this.players[i].hide_cards();
		    				}
		    				repaint();
		    			}
	    			}
	    		}
	    	}
    	}
    }
    
    public boolean action(Event e, Object args) {
    	if(e.target == b_login) {
    		// Submit on login button
    		
    		// Get the username and password
    		username = t_username.getText();
    		password = t_password.getText();
    		player = requestLogin(username, password);
    		
    		if(player == null) {
    			state = FAILED_LOGIN;
    			state_changed();
    		} else {
    			rooms = getRooms();
    			if(rooms != null) {
    				state = VIEW_ROOMS;
        			state_changed();
    			}
    		}
    		repaint();
    	}
    	if(e.target == b_new_room) {
    		room = createRoom();
    		if(room != null) {
    			if(enterRoom(room)) {
    				state = IN_GAME;
    				state_changed();
    			}
    		}
    		repaint();
    	}
    	if(e.target == b_choose_room) {
    		room = getRoom(t_room.getText());
    		if(room != null) {
    			if(enterRoom(room)) {
    				state = IN_GAME;
    				state_changed();
    			}
    		}
    		repaint();
    	}
    	if(e.target == b_bet) {
    		String value = t_bet_sum.getText();
    		int v = 0;
    		try {
    			v = Integer.parseInt(value);
    		} catch(Exception ex) {}
    		if(v>0) {
	    		try {
	    			room.bet(username, password, v);
	    		} catch(RemoteException ex) {
					System.out.println("Eroare");
				}
    		}
    	}
    	if(e.target == b_hit_me) {
    		try {
    			room.hitMe(username, password);
    		} catch(RemoteException ex) {
				System.out.println("Eroare");
			}
    	}
    	if(e.target == b_stand) {
    		try {
    			room.enough(username, password);
    		} catch(RemoteException ex) {
				System.out.println("Eroare");
			}
    	}
    	if(e.target == b_double) {
    		try {
    			room.betDouble(username, password);
    		} catch(RemoteException ex) {
				System.out.println("Eroare");
			}
    	}
    	if(e.target == b_double) {
    		try {
    			String suma = t_bet_sum.getText();
    			room.bet(username, password, Integer.parseInt(suma));
    		} catch(RemoteException ex) {
				System.out.println("Eroare");
			}
    	}
    	if(e.target == b_leave_room) {
    		try {
    			room.leave(username, password);
    		} catch(RemoteException ex) {
				System.out.println("Eroare");
			}
    		room = null;
    		rooms = getRooms();
			if(rooms != null) {
				state = VIEW_ROOMS;
    			state_changed();
			}
			repaint();
    	}
    	
    	return true;
    }
}
